home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / utilities / isfast / libpdb / pdb.c < prev    next >
C/C++ Source or Header  |  1996-11-11  |  20KB  |  806 lines

  1. /*****************************************************************************
  2.  * pdb - routines for maintaining a database of performance information
  3.  *
  4.  * Porting Notes:
  5.  *
  6.  *    ANSI C (including library routines specified by the standard) is
  7.  *    used throughout.
  8.  *
  9.  *    The routines GetDBFileName() and GetDefaultMachineName() should be
  10.  *    modified when porting to non-UNIX systems.
  11.  *
  12.  *    There are a few ``X-isms'' in this code; for example, the use of
  13.  *    the DISPLAY environment variable to fetch the default machine
  14.  *    name.  These would have to be changed when porting to OS/2 or
  15.  *    Windows.
  16.  *
  17.  *    GetClock() should be modified when porting to non-SVR4 systems, or
  18.  *    to systems whose "double" type has fewer bits of mantissa than
  19.  *    specified by IEEE 754.
  20.  *
  21.  * History:
  22.  *
  23.  *    1.0    9/93    akin    Written.  See accompanying README for
  24.  *                rationale and examples.
  25.  *    2.0    3/95    akin    Added support for version strings
  26.  *                (necessitating a new database file and
  27.  *                format) and made calibration optional in
  28.  *                pdbMeasureRate().  Allowed more special
  29.  *                characters in strings.  Cleaned up some
  30.  *                nits.
  31.  *****************************************************************************/
  32.  
  33.  
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <sys/time.h>    /* for gettimeofday, used by GetClock */
  38. #include "pdb.h"
  39.  
  40.  
  41. typedef struct HashNodeS {
  42.     struct HashNodeS*    next;
  43.     char*            machineName;
  44.     char*            applicationName;
  45.     char*            benchmarkName;
  46.     char*            versionString;
  47.     double            rate;
  48.     char            storage[1];
  49.     } HashNodeT;
  50. static HashNodeT** HashTable = NULL;
  51. #define HASH_STEP(hash,c) (hash)+=(c)
  52. #define HASH_TABLE_SIZE 64
  53. #define HASH_MODULUS(hash) (hash)&=(HASH_TABLE_SIZE-1)
  54.  
  55.  
  56. static int    Dirty = 0;
  57. static char*    Hex = "0123456789ABCDEF";
  58. static double    Overhead = 0.0;
  59.  
  60.  
  61. #define LINE_MAX 1024
  62.  
  63.  
  64. static double      ChooseRunTime        (void);
  65. static pdbStatusT DumpHashTable        (FILE*        dbFile);
  66. static void      FinalizeHashTable    (void);
  67. static double      GetClock        (void);
  68. static void      GetDBFileName        (char*        name);
  69. static void      GetDefaultMachineName    (char*        name);
  70. static pdbStatusT InitializeHashTable    (void);
  71. static pdbStatusT InsertHashNode    (const char*    machineName,
  72.                      int        machineNameLength,
  73.                      const char*    applicationName,
  74.                      int        applicationNameLength,
  75.                      const char*    benchmarkName,
  76.                      int        benchmarkNameLength,
  77.                      const char*    versionString,
  78.                      int        versionStringLength,
  79.                      double        rate,
  80.                      unsigned    hash);
  81. static pdbStatusT LoadHashTable        (FILE*        dbFile);
  82. static pdbStatusT LookupHashNode    (HashNodeT**    nodeP,
  83.                      const char*    machineName,
  84.                      const char*    applicationName,
  85.                      const char*    benchmarkName,
  86.                      const char*    versionString,
  87.                      int        createWhenMissing);
  88. static void      StringToToken        (char*        dst,
  89.                      char*        src);
  90. static char*      TokenToString        (char*        dst,
  91.                      char*        src);
  92. static double      WaitForTick        (void);
  93.  
  94.  
  95. /*****************************************************************************
  96.  * ChooseRunTime - select an appropriate runtime for benchmarks
  97.  *****************************************************************************/
  98.  
  99. static double
  100. ChooseRunTime(void) {
  101.     double start;
  102.     double finish;
  103.     double        runTime;
  104.  
  105.     start = GetClock();
  106.  
  107.     /* Wait for next tick: */
  108.     while ((finish = GetClock()) == start)
  109.         ;
  110.     
  111.     /* Run for 100 ticks, clamped to [1.0 sec, 5.0 sec]: */
  112.     runTime = 100.0 * (finish - start);
  113.     if (runTime < 1.0)
  114.         runTime = 1.0;
  115.     else if (runTime > 5.0)
  116.         runTime = 5.0;
  117.  
  118.     return runTime;
  119.     }
  120.  
  121.  
  122. /*****************************************************************************
  123.  * DumpHashTable - write contents of hash table to database file
  124.  *****************************************************************************/
  125.  
  126. static pdbStatusT
  127. DumpHashTable(FILE* dbFile) {
  128.     int        i;
  129.     HashNodeT*    n;
  130.     char machineName[LINE_MAX];
  131.     char applicationName[LINE_MAX];
  132.     char benchmarkName[LINE_MAX];
  133.     char versionString[LINE_MAX];
  134.  
  135.     if (!HashTable)
  136.         return PDB_NOT_OPEN;
  137.  
  138.     for (i = 0; i < HASH_TABLE_SIZE; ++i)
  139.         for (n = HashTable[i]; n; n = n->next) {
  140.             StringToToken(machineName, n->machineName);
  141.             StringToToken(applicationName, n->applicationName);
  142.             StringToToken(benchmarkName, n->benchmarkName);
  143.             StringToToken(versionString, n->versionString);
  144.             fprintf(dbFile, "%s\t%s\t%s\t%s\t%g\n",
  145.                 machineName,
  146.                 applicationName,
  147.                 benchmarkName,
  148.                 versionString,
  149.                 n->rate);
  150.             }
  151.  
  152.     return PDB_NO_ERROR;
  153.     }
  154.  
  155.  
  156. /*****************************************************************************
  157.  * FinalizeHashTable - deallocate all storage used by the hash table
  158.  *****************************************************************************/
  159.  
  160. static void
  161. FinalizeHashTable(void) {
  162.     int        i;
  163.     HashNodeT*    h;
  164.     HashNodeT*    next;
  165.  
  166.     if (!HashTable)
  167.         return;
  168.  
  169.     for (i = HASH_TABLE_SIZE - 1; i >= 0; --i)
  170.         for (h = HashTable[i]; h; h = next) {
  171.             next = h->next;
  172.             free(h);
  173.             }
  174.     
  175.     free(HashTable);
  176.     HashTable = NULL;
  177.     }
  178.  
  179.  
  180. /*****************************************************************************
  181.  * GetClock - get current time (expressed in seconds)
  182.  *****************************************************************************/
  183.  
  184. static double
  185. GetClock(void) {
  186.     struct timeval t;
  187.  
  188.     gettimeofday(&t);
  189.  
  190.     return (double) t.tv_sec + (double) t.tv_usec * 1E-6;
  191.     }
  192.  
  193.  
  194. /*****************************************************************************
  195.  * GetDBFileName - get full pathname of performance database file
  196.  *****************************************************************************/
  197.  
  198. static void
  199. GetDBFileName(char* name) {
  200.     char* home;
  201.  
  202.     if (home = getenv("HOME"))
  203.         strcpy(name, home);
  204.     else
  205.         name[0] = '\0';
  206.     strcat(name, "/.pdb2");
  207.     }
  208.  
  209.  
  210. /*****************************************************************************
  211.  * GetDefaultMachineName - return name of "current" machine
  212.  *****************************************************************************/
  213.  
  214. static void
  215. GetDefaultMachineName(char* name) {
  216.     char* display;
  217.  
  218.     if (display = getenv("DISPLAY"))
  219.         strcpy(name, display);
  220.     else
  221.         strcpy(name, ":0");
  222.     }
  223.  
  224.  
  225. /*****************************************************************************
  226.  * InitializeHashTable - allocate memory for hash table and initialize it
  227.  *****************************************************************************/
  228.  
  229. static pdbStatusT
  230. InitializeHashTable(void) {
  231.     int i;
  232.  
  233.     HashTable = (HashNodeT**)
  234.         malloc(HASH_TABLE_SIZE * sizeof(HashNodeT*));
  235.     if (!HashTable)
  236.         return PDB_OUT_OF_MEMORY;
  237.  
  238.     for (i = HASH_TABLE_SIZE - 1; i >= 0; --i)
  239.         HashTable[i] = NULL;
  240.     
  241.     return PDB_NO_ERROR;
  242.     }
  243.  
  244.  
  245. /*****************************************************************************
  246.  * InsertHashNode - place key and data in a selected bucket of the hash table
  247.  *
  248.  * Note:  Does not check for duplicates.
  249.  *      String length arguments *include* the zero byte at the end of the
  250.  *        string; e.g. "abc" would have length 4.
  251.  *****************************************************************************/
  252.  
  253. static pdbStatusT
  254. InsertHashNode (
  255.     const char*    machineName,
  256.     int        machineNameLength,
  257.     const char*    applicationName,
  258.     int        applicationNameLength,
  259.     const char*    benchmarkName,
  260.     int        benchmarkNameLength,
  261.     const char*    versionString,
  262.     int        versionStringLength,
  263.     double    rate,
  264.     unsigned    hash
  265.     ) {
  266.     HashNodeT*    n;
  267.  
  268.     if (!HashTable)
  269.         return PDB_NOT_OPEN;
  270.  
  271.     n = (HashNodeT*) malloc(sizeof(HashNodeT) + machineNameLength
  272.         + applicationNameLength + benchmarkNameLength
  273.         + versionStringLength);
  274.     if (!n)
  275.         return PDB_OUT_OF_MEMORY;
  276.  
  277.     n->machineName = n->storage;
  278.     memcpy(n->machineName, machineName, machineNameLength);
  279.     n->applicationName = n->machineName + machineNameLength;
  280.     memcpy(n->applicationName, applicationName, applicationNameLength);
  281.     n->benchmarkName = n->applicationName + applicationNameLength;
  282.     memcpy(n->benchmarkName, benchmarkName, benchmarkNameLength);
  283.     n->versionString = n->benchmarkName + benchmarkNameLength;
  284.     memcpy(n->versionString, versionString, versionStringLength);
  285.     n->rate = rate;
  286.  
  287.     n->next = HashTable[hash];
  288.     HashTable[hash] = n;
  289.  
  290.     return PDB_NO_ERROR;
  291.     }
  292.  
  293.  
  294. /*****************************************************************************
  295.  * LoadHashTable - load hash table with contents of performance database file
  296.  *****************************************************************************/
  297.  
  298. static pdbStatusT
  299. LoadHashTable(FILE* dbFile) {
  300.     char        line[LINE_MAX];
  301.     pdbStatusT    error = PDB_NO_ERROR;
  302.  
  303.     if (!HashTable)
  304.         return PDB_NOT_OPEN;
  305.  
  306.     while (fgets(line, sizeof(line), dbFile)) {
  307.         char        machineName[LINE_MAX];
  308.         char        applicationName[LINE_MAX];
  309.         char        benchmarkName[LINE_MAX];
  310.         char        versionString[LINE_MAX];
  311.         int        machineNameLength;
  312.         int        applicationNameLength;
  313.         int        benchmarkNameLength;
  314.         int        versionStringLength;
  315.         double        rate;
  316.         char*        p;
  317.         char*        q;
  318.         int        c;
  319.         unsigned    hash;
  320.  
  321.  
  322.         p = line;
  323.         hash = 0;
  324.  
  325.         /* Skip whitespace before machine name: */
  326.         while (isspace(*p))
  327.             ++p;
  328.         if (!*p) {
  329.             error |= PDB_SYNTAX_ERROR;
  330.             continue;
  331.             }
  332.  
  333.         /* Scan machine name, get length and hash value: */
  334.         p = TokenToString(machineName, p);
  335.         for (q = machineName; c = *q++;)
  336.             HASH_STEP(hash, c);
  337.         machineNameLength = q - machineName;    /* includes '\0' */
  338.  
  339.         /* Skip whitespace before application name: */
  340.         while (isspace(*p))
  341.             ++p;
  342.         if (!*p) {
  343.             error |= PDB_SYNTAX_ERROR;
  344.             continue;
  345.             }
  346.  
  347.         /* Scan application name, get length and hash: */
  348.         p = TokenToString(applicationName, p);
  349.         for (q = applicationName; c = *q++;)
  350.             HASH_STEP(hash, c);
  351.         applicationNameLength = q - applicationName; /* includes '\0' */
  352.         
  353.         /* Skip whitespace before benchmark name: */
  354.         while (isspace(*p))
  355.             ++p;
  356.         if (!*p) {
  357.             error |= PDB_SYNTAX_ERROR;
  358.             continue;
  359.             }
  360.  
  361.         /* Scan benchmark name, get length and hash: */
  362.         p = TokenToString(benchmarkName, p);
  363.         for (q = benchmarkName; c = *q++;)
  364.             HASH_STEP(hash, c);
  365.         benchmarkNameLength = q - benchmarkName; /* includes '\0' */
  366.         
  367.         /* Skip whitespace before version string: */
  368.         while (isspace(*p))
  369.             ++p;
  370.         if (!*p) {
  371.             error |= PDB_SYNTAX_ERROR;
  372.             continue;
  373.             }
  374.  
  375.         /* Scan version string, get length and hash: */
  376.         p = TokenToString(versionString, p);
  377.         for (q = versionString; c = *q++;)
  378.             HASH_STEP(hash, c);
  379.         versionStringLength = q - versionString; /* includes '\0' */
  380.  
  381.         /* Finally, get the rate: */
  382.         rate = strtod(p, NULL);
  383.         if (rate <= 0.0)
  384.             error |= PDB_SYNTAX_ERROR;    /* probably */
  385.  
  386.  
  387.         HASH_MODULUS(hash);
  388.  
  389.  
  390.         /* Note that we don't weed out any duplicates here... */
  391.  
  392.         error |= InsertHashNode(
  393.             machineName, machineNameLength,
  394.             applicationName, applicationNameLength,
  395.             benchmarkName, benchmarkNameLength,
  396.             versionString, versionStringLength,
  397.             rate, hash);
  398.         }
  399.  
  400.     return error;
  401.     }
  402.  
  403.     
  404. /*****************************************************************************
  405.  * LookupHashNode - find key/data node in hash table, or insert it if needed
  406.  *****************************************************************************/
  407.  
  408. static pdbStatusT
  409. LookupHashNode (
  410.     HashNodeT**    nodeP,
  411.     const char*    machineName,
  412.     const char*    applicationName,
  413.     const char*    benchmarkName,
  414.     const char* versionString,
  415.     int        createWhenMissing
  416.     ) {
  417.     char        defaultMachineName[LINE_MAX];
  418.     const char*    p;
  419.     int        c;
  420.     unsigned    hash;
  421.     HashNodeT*    node;
  422.     int        machineNameLength;
  423.     int        applicationNameLength;
  424.     int        benchmarkNameLength;
  425.     int        versionStringLength;
  426.     pdbStatusT    error;
  427.  
  428.     if (!HashTable)
  429.         return PDB_NOT_OPEN;
  430.  
  431.     if (!machineName) {
  432.         GetDefaultMachineName(defaultMachineName);
  433.         machineName = defaultMachineName;
  434.         }
  435.  
  436.     hash = 0;
  437.  
  438.     for (p = machineName; c = *p++;)
  439.         HASH_STEP(hash, c);
  440.     machineNameLength = p - machineName;    /* includes '\0' at end */
  441.     for (p = applicationName; c = *p++;)
  442.         HASH_STEP(hash, c);
  443.     applicationNameLength = p - applicationName;
  444.     for (p = benchmarkName; c = *p++;)
  445.         HASH_STEP(hash, c);
  446.     benchmarkNameLength = p - benchmarkName;
  447.     for (p = versionString; c = *p++;)
  448.         HASH_STEP(hash, c);
  449.     versionStringLength = p - versionString;
  450.     
  451.     HASH_MODULUS(hash);
  452.  
  453.     for (node = HashTable[hash]; node; node = node->next)
  454.         if (!strcmp(node->machineName, machineName)
  455.          && !strcmp(node->applicationName, applicationName)
  456.          && !strcmp(node->benchmarkName, benchmarkName)
  457.          && !strcmp(node->versionString, versionString)) {
  458.             *nodeP = node;
  459.             return PDB_NO_ERROR;
  460.             }
  461.  
  462.     if (createWhenMissing) {
  463.         error = InsertHashNode(
  464.             machineName, machineNameLength,
  465.             applicationName, applicationNameLength,
  466.             benchmarkName, benchmarkNameLength,
  467.             versionString, versionStringLength,
  468.             0.0, hash);
  469.         *nodeP = HashTable[hash];
  470.         return error;
  471.         }
  472.     else
  473.         return PDB_NOT_FOUND;
  474.     }
  475.  
  476.  
  477. /*****************************************************************************
  478.  * StringToToken - convert string to a token with escape sequences
  479.  *****************************************************************************/
  480.  
  481. static void
  482. StringToToken(char* dst, char* src) {
  483.     char c;
  484.  
  485.     while (c = *src++)
  486.         if (c == '\\') {
  487.             *dst++ = c;
  488.             *dst++ = c;
  489.             }
  490.         else if (isgraph(c))
  491.             *dst++ = c;
  492.         else
  493.             switch (c) {
  494.                 case ' ':
  495.                     *dst++ = '\\';
  496.                     *dst++ = ' ';
  497.                     break;
  498.                 case '\t':
  499.                     *dst++ = '\\';
  500.                     *dst++ = 't';
  501.                     break;
  502.                 case '\r':
  503.                     *dst++ = '\\';
  504.                     *dst++ = 'r';
  505.                     break;
  506.                 case '\n':
  507.                     *dst++ = '\\';
  508.                     *dst++ = 'n';
  509.                     break;
  510.                 case '\v':
  511.                     *dst++ = '\\';
  512.                     *dst++ = 'v';
  513.                     break;
  514.                 case '\f':
  515.                     *dst++ = '\\';
  516.                     *dst++ = 'f';
  517.                     break;
  518.                 case '\b':
  519.                     *dst++ = '\\';
  520.                     *dst++ = 'b';
  521.                     break;
  522.                 case '\a':
  523.                     *dst++ = '\\';
  524.                     *dst++ = 'a';
  525.                     break;
  526.                 default:
  527.                     *dst++ = '\\';
  528.                     *dst++ = 'x';
  529.                     *dst++ = Hex[(c >> 4) & 0xF];
  530.                     *dst++ = Hex[c & 0xF];
  531.                     break;
  532.                 }
  533.     *dst++ = 0;
  534.     }
  535.  
  536.  
  537. /*****************************************************************************
  538.  * TokenToString - convert space-delimited token to string, interpreting
  539.  *    escape sequences
  540.  *****************************************************************************/
  541.  
  542. static char*
  543. TokenToString(char* dst, char* src) {
  544.     char c;
  545.  
  546.     while (c = *src++)
  547.         if (c == '\\')
  548.             switch (c = *src++) {
  549.                 case '\\':
  550.                 case ' ':
  551.                     *dst++ = c;
  552.                     break;
  553.                 case 't':
  554.                     *dst++ = '\t';
  555.                     break;
  556.                 case 'r':
  557.                     *dst++ = '\r';
  558.                     break;
  559.                 case 'n':
  560.                     *dst++ = '\n';
  561.                     break;
  562.                 case 'v':
  563.                     *dst++ = '\v';
  564.                     break;
  565.                 case 'f':
  566.                     *dst++ = '\f';
  567.                     break;
  568.                 case 'b':
  569.                     *dst++ = '\b';
  570.                     break;
  571.                 case 'a':
  572.                     *dst++ = '\a';
  573.                     break;
  574.                 case 'x':
  575.                     c = *src++;
  576.                     *dst = (strchr(Hex, c) - Hex) << 4;
  577.                     c = *src++;
  578.                     *dst++ |= (strchr(Hex, c) - Hex);
  579.                     break;
  580.                 default:
  581.                     *dst++ = '\\';
  582.                     *dst++ = c;
  583.                     break;
  584.                 }
  585.         else if (isspace(c))
  586.             break;
  587.         else
  588.             *dst++ = c;
  589.     *dst++ = 0;
  590.     return src;
  591.     }
  592.  
  593.  
  594. /*****************************************************************************
  595.  * WaitForTick - wait for beginning of next system clock tick; return the time
  596.  *****************************************************************************/
  597.  
  598. static double
  599. WaitForTick(void) {
  600.     double start;
  601.     double current;
  602.  
  603.     start = GetClock();
  604.  
  605.     /* Wait for next tick: */
  606.     while ((current = GetClock()) == start)
  607.         ;
  608.  
  609.     /* Start timing: */
  610.     return current;
  611.     }
  612.  
  613.  
  614. /*****************************************************************************
  615.  * pdbClose - write perf data to database file if necessary, then clean up
  616.  *****************************************************************************/
  617.  
  618. pdbStatusT
  619. pdbClose(void) {
  620.     pdbStatusT    error = PDB_NO_ERROR;
  621.     char        dbFileName[FILENAME_MAX];
  622.     FILE*        dbFile;
  623.  
  624.     if (!HashTable)
  625.         return PDB_NOT_OPEN;
  626.  
  627.     if (Dirty) {
  628.         GetDBFileName(dbFileName);
  629.  
  630.         if (dbFile = fopen(dbFileName, "w")) {
  631.             error = DumpHashTable(dbFile);
  632.             fclose(dbFile);
  633.             }
  634.         else
  635.             error = PDB_CANT_WRITE;
  636.  
  637.         Dirty = 0;
  638.         }
  639.  
  640.     FinalizeHashTable();
  641.  
  642.     return error;
  643.     }
  644.  
  645.  
  646. /*****************************************************************************
  647.  * pdbMeasureRate - measure number of caller's operations performed per second
  648.  *****************************************************************************/
  649.  
  650. pdbStatusT
  651. pdbMeasureRate (
  652.     pdbCallbackT    initialize,
  653.     pdbCallbackT    operation,
  654.     pdbCallbackT    finalize,
  655.     int            calibrate,
  656.     double*        rate
  657.     ) {
  658.     double        runTime;
  659.     long        reps;
  660.     long        i;
  661.     double        start;
  662.     double        current;
  663.  
  664.  
  665.     if (!operation) {
  666.         *rate = 0.0;
  667.         return PDB_NO_ERROR;
  668.         }
  669.  
  670.  
  671.     /* Select a run time that's appropriate for our timer resolution: */
  672.     runTime = ChooseRunTime();
  673.  
  674.  
  675.     /* Measure approximate overhead for finalization and timing routines: */
  676.     if (calibrate) {
  677.         if (initialize)
  678.             (*initialize)();
  679.         reps = 0;
  680.         start = WaitForTick();
  681.         do {
  682.             if (finalize)
  683.                 (*finalize)();
  684.             ++reps;
  685.             } while ((current = GetClock()) < start + runTime);
  686.         Overhead = (current - start) / (double) reps;
  687.         }
  688.  
  689.  
  690.     /*
  691.      * Measure successively larger batches of operations until we find
  692.      * one that's long enough to meet our runtime target:
  693.      */
  694.     reps = 1;
  695.     for (;;) {
  696.         if (initialize)
  697.             (*initialize)();
  698.  
  699.         start = WaitForTick();
  700.  
  701.         for (i = reps; i > 0; --i)
  702.             (*operation)();
  703.  
  704.         if (finalize)
  705.             (*finalize)();
  706.  
  707.         current = GetClock();
  708.         if (current >= start + runTime + Overhead)
  709.             break;
  710.  
  711.         /* Try to reach runtime target in one fell swoop: */
  712.         if (current > start)
  713.             reps *= 0.5 + runTime / (current - start - Overhead);
  714.         else
  715.             reps *= 2;
  716.         }
  717.  
  718.     /* Subtract overhead to determine the final operation rate: */
  719.     *rate = (double) reps / (current - start - Overhead);
  720.     return PDB_NO_ERROR;
  721.     }
  722.  
  723.  
  724. /*****************************************************************************
  725.  * pdbOpen - open perf database file, load contents into memory
  726.  *****************************************************************************/
  727.  
  728. pdbStatusT
  729. pdbOpen(void) {
  730.     pdbStatusT    error;
  731.     char        dbFileName[FILENAME_MAX];
  732.     FILE*        dbFile;
  733.  
  734.     if (HashTable)
  735.         return PDB_ALREADY_OPEN;
  736.  
  737.     if (error = InitializeHashTable())
  738.         return error;
  739.  
  740.     /* If the database file can be read, load its contents: */
  741.     GetDBFileName(dbFileName);
  742.     if (dbFile = fopen(dbFileName, "r")) {
  743.         error = LoadHashTable(dbFile);
  744.         fclose(dbFile);
  745.         }
  746.  
  747.     /* The database is "clean" unless pdbWriteRate() is called: */
  748.     Dirty = 0;
  749.  
  750.     return error;
  751.     }
  752.  
  753.  
  754. /*****************************************************************************
  755.  * pdbReadRate - return performance for a given machine, app, benchmark,
  756.  *    and version
  757.  *****************************************************************************/
  758.  
  759. pdbStatusT
  760. pdbReadRate (
  761.     const char*        machineName,
  762.     const char*        applicationName,
  763.     const char*        benchmarkName,
  764.     const char*        versionString,
  765.     double*        rate
  766.     ) {
  767.     HashNodeT*    node;
  768.     pdbStatusT    error;
  769.  
  770.     error = LookupHashNode(&node, machineName, applicationName,
  771.         benchmarkName, versionString, 0);
  772.         /* don't create node if it's not present */
  773.     if (!error)
  774.         *rate = node->rate;
  775.  
  776.     return error;
  777.     }
  778.  
  779.  
  780. /*****************************************************************************
  781.  * pdbWriteRate - save performance data for a given machine, app, benchmark,
  782.  *    and version
  783.  *****************************************************************************/
  784.  
  785. pdbStatusT
  786. pdbWriteRate (
  787.     const char*        machineName,
  788.     const char*        applicationName,
  789.     const char*        benchmarkName,
  790.     const char*        versionString,
  791.     const double    rate
  792.     ) {
  793.     HashNodeT*    node;
  794.     pdbStatusT    error;
  795.  
  796.     Dirty = 1;
  797.  
  798.     error = LookupHashNode(&node, machineName, applicationName,
  799.        benchmarkName, versionString, 1);
  800.        /* create node if it's not already in table */
  801.     if (!error)
  802.         node->rate = rate;
  803.  
  804.     return error;
  805.     }
  806.